﻿#include "stdafx.h"
#include "scriptwstring.h"

using namespace std;

BEGIN_AS_NAMESPACE

//--------------
// constructors
//--------------

CScriptWString::CScriptWString()
{
	// Count the first reference
	refCount = 1;
}

CScriptWString::CScriptWString(const TCHAR *s)
{
	refCount = 1;
	buffer = s;
}

CScriptWString::CScriptWString(const wstring &s)
{
	refCount = 1;
	buffer = s;
}

CScriptWString::CScriptWString(const CScriptWString &s)
{
	refCount = 1;
	buffer = s.buffer;
}
CScriptWString::CScriptWString(const CScriptString &other)
{
	refCount = 1;
	*this = other;
}


CScriptWString::~CScriptWString()
{
	assert( refCount == 0 );
}

//--------------------
// reference counting
//--------------------

void CScriptWString::AddRef()
{
	refCount++;
}


void CScriptWString::Release()
{
	if( --refCount == 0 )
		delete this;
}

//-----------------
// wstring = wstring
//-----------------

CScriptWString &CScriptWString::operator=(const CScriptWString &other)
{
	// Copy only the buffer, not the reference counter
	buffer = other.buffer;

	// Return a reference to this object
	return *this;
}
CScriptWString &CScriptWString::operator=(const CScriptString &s)
{
	NatRacing::CodeConvert::Instance()->MultiByte2Unicode(s.buffer.c_str(),this->buffer,-1);
	return *this;
}

//------------------
// wstring += wstring
//------------------

CScriptWString &CScriptWString::operator+=(const CScriptWString &other)
{
	buffer += other.buffer;
	return *this;
}

int CScriptWString::opCmp( const CScriptWString& other )
{
	if(buffer<other.buffer) return -1;
	else if(buffer>other.buffer) return 1;
	return 0;
}

//-----------------
// wstring + wstring
//-----------------

CScriptWString *operator+(const CScriptWString &a, const CScriptWString &b)
{
	// Return a new object as a script handle
	return new CScriptWString(a.buffer + b.buffer);
}

//----------------
// wstring = value
//----------------

static CScriptWString &AssignUIntToString(unsigned int i, CScriptWString &dest)
{
	dest.buffer =boost::lexical_cast<wstring>(i);
	return dest;
}

static CScriptWString &AssignIntToString(int i, CScriptWString &dest)
{
	dest.buffer =boost::lexical_cast<wstring>(i);
	return dest;
}

static CScriptWString &AssignFloatToString(float f, CScriptWString &dest)
{
	dest.buffer =boost::lexical_cast<wstring>(f);
	return dest;
}
static CScriptWString &AssignFloatToStringDigits(float f,UINT digits, CScriptWString &dest)
{
	wstringstream ss;
	ss.precision(digits);
	ss<<f;
	ss>>dest.buffer;
	return dest;
}
static CScriptWString &AssignDoubleToString(double f, CScriptWString &dest)
{
	dest.buffer =boost::lexical_cast<wstring>(f);
	return dest;
}

//-----------------
// wstring += value
//-----------------

static CScriptWString &AddAssignUIntToString(unsigned int i, CScriptWString &dest)
{
	wstring buf =boost::lexical_cast<wstring>(i);
	dest.buffer += buf;
	return dest;
}

static CScriptWString &AddAssignIntToString(int i, CScriptWString &dest)
{
	wstring buf =boost::lexical_cast<wstring>(i);
	dest.buffer += buf;
	return dest;
}

static CScriptWString &AddAssignFloatToString(float f, CScriptWString &dest)
{
	wstring buf =boost::lexical_cast<wstring>(f);
	dest.buffer += buf;
	return dest;
}

static CScriptWString &AddAssignDoubleToString(double f, CScriptWString &dest)
{
	wstring buf =boost::lexical_cast<wstring>(f);
	dest.buffer += buf;
	return dest;
}

//----------------
// wstring + value
//----------------

static CScriptWString *AddStringUInt(const CScriptWString &str, unsigned int i)
{
	wstring buf =boost::lexical_cast<wstring>(i);
	return new CScriptWString(str.buffer + buf);
}



static CScriptWString *AddStringInt(const CScriptWString &str, int i)
{
	wstring buf =boost::lexical_cast<wstring>(i);
	return new CScriptWString(str.buffer + buf);
}



static CScriptWString *AddStringFloat(const CScriptWString &str, float f)
{
	wstring buf =boost::lexical_cast<wstring>(f);
	return new CScriptWString(str.buffer + buf);
}



static CScriptWString *AddStringDouble(const CScriptWString &str, double f)
{
	wstring buf =boost::lexical_cast<wstring>(f);
	return new CScriptWString(str.buffer + buf);

}


//----------------
// value + wstring
//----------------

static CScriptWString *AddIntString(int i, const CScriptWString &str)
{
	wstring buf =boost::lexical_cast<wstring>(i);
	return new CScriptWString(str.buffer + buf);

}


static CScriptWString *AddUIntString(unsigned int i, const CScriptWString &str)
{
	wstring buf =boost::lexical_cast<wstring>(i);
	return new CScriptWString(str.buffer + buf);

}


static CScriptWString *AddFloatString(float f, const CScriptWString &str)
{
	wstring buf =boost::lexical_cast<wstring>(f);
	return new CScriptWString(str.buffer + buf);

}


static CScriptWString *AddDoubleString(double f, const CScriptWString &str)
{
	wstring buf =boost::lexical_cast<wstring>(f);
	return new CScriptWString(str.buffer + buf);

}

//----------
// wstring[]
//----------

static TCHAR *StringCharAt(unsigned int i, CScriptWString &str)
{
	if( i >= str.buffer.size() )
	{
		// Set a script exception
		asIScriptContext *ctx = asGetActiveContext();
		ctx->SetException("Out of range");

		// Return a null pointer
		return 0;
	}

	return &str.buffer[i];
}



//-----------------------
// AngelScript functions
//-----------------------

// This is the default wstring factory, that is responsible for creating empty wstring objects, e.g. when a variable is declared
static CScriptWString *StringDefaultFactory()
{
	// Allocate and initialize with the default constructor BECodeDefaultFactory
	return new CScriptWString();
}

static CScriptWString *StringCopyFactory(const CScriptWString &other)
{
	// Allocate and initialize with the copy constructor
	return new CScriptWString(other);
}
static CScriptWString *StringCopySFactory(const CScriptString &other)
{
	// Allocate and initialize with the copy constructor
	return new CScriptWString(other);
}
template<typename T>
static T wsting2value(CScriptWString &self)
{
	return boost::lexical_cast<T>(self.buffer);	
}
template<typename T>
static bool canWstingCast2value(CScriptWString &self)
{
	try
	{
		T t=boost::lexical_cast<T>(self.buffer);	
	}
	catch (boost::bad_lexical_cast & e)
	{
		e=e;
		return false;
	}
	return true;

}
// This is where we register the wstring type
void RegisterScriptWString_Native(asIScriptEngine *engine)
{
	int r;

	//TCHAR
	//r = engine->RegisterObjectType("TCHAR", sizeof(TCHAR), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_PRIMITIVE); assert( r >= 0 );

	// Register the type
	r = engine->RegisterObjectType("wstring", sizeof(CScriptWString), asOBJ_REF); assert( r >= 0 );

	// Register the object operator overloads
	// Note: We don't have to register the destructor, since the object uses reference counting
	r = engine->RegisterObjectBehaviour("wstring", asBEHAVE_FACTORY,    "wstring @f()",                 asFUNCTION(StringDefaultFactory), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("wstring", asBEHAVE_FACTORY,    "wstring @f(const wstring &in)", asFUNCTION(StringCopyFactory), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("wstring", asBEHAVE_FACTORY,    "wstring @f(const string &in)", asFUNCTION(StringCopySFactory), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("wstring", asBEHAVE_ADDREF,     "void f()",                    asMETHOD(CScriptWString,AddRef), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("wstring", asBEHAVE_RELEASE,    "void f()",                    asMETHOD(CScriptWString,Release), asCALL_THISCALL); assert( r >= 0 );
	//r = engine->RegisterObjectBehaviour("wstring", asBEHAVE_ASSIGNMENT, "wstring &f(const wstring &in)", asMETHODPR(CScriptWString, operator =, (const CScriptWString&), CScriptWString&), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("wstring", "wstring& opAssign(const wstring &in)",asMETHODPR(CScriptWString, operator =, (const CScriptWString&), CScriptWString&), asCALL_THISCALL);assert( r >= 0 );
	//r = engine->RegisterObjectBehaviour("wstring", asBEHAVE_ADD_ASSIGN, "wstring &f(const wstring &in)", asMETHODPR(CScriptWString, operator+=, (const CScriptWString&), CScriptWString&), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("wstring", "wstring& opAddAssign(const wstring&in)",asMETHODPR(CScriptWString, operator+=, (const CScriptWString&), CScriptWString&), asCALL_THISCALL);assert( r >= 0 );
	//将string转换为wstring,这样脚本就等于支持unicode了
	r = engine->RegisterObjectMethod("wstring", "wstring& opAssign(const string &in)",asMETHODPR(CScriptWString, operator =, (const CScriptString&), CScriptWString&), asCALL_THISCALL);assert( r >= 0 );
	//r = engine->RegisterObjectBehaviour("wstring", asBEHAVE_ASSIGNMENT, "wstring &f(const string &in)", asMETHODPR(CScriptWString, operator =, (const CScriptString&), CScriptWString&), asCALL_THISCALL); assert( r >= 0 );

	// Register the global operator overloads
	// Note: We can use std::wstring's methods directly because the
	// internal std::wstring is placed at the beginning of the class
	r = engine->RegisterObjectMethod("wstring", "bool opEquals(const wstring &in) const",asFUNCTIONPR(operator==, (const wstring &,const wstring &), bool), asCALL_CDECL_OBJFIRST);assert( r >= 0 );
	//r = engine->RegisterGlobalBehaviour(asBEHAVE_EQUAL,       "bool f(const wstring &in, const wstring &in)",    asFUNCTIONPR(operator==, (const wstring &, const wstring &), bool), asCALL_CDECL); assert( r >= 0 );
	//r = engine->RegisterGlobalBehaviour(asBEHAVE_NOTEQUAL,    "bool f(const wstring &in, const wstring &in)",    asFUNCTIONPR(operator!=, (const wstring &, const wstring &), bool), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("wstring", "int opCmp(const wstring &in) const",asMETHOD(CScriptWString,opCmp), asCALL_THISCALL);assert( r >= 0 );
	//r = engine->RegisterGlobalBehaviour(asBEHAVE_LEQUAL,      "bool f(const wstring &in, const wstring &in)",    asFUNCTIONPR(operator<=, (const wstring &, const wstring &), bool), asCALL_CDECL); assert( r >= 0 );
	//r = engine->RegisterGlobalBehaviour(asBEHAVE_GEQUAL,      "bool f(const wstring &in, const wstring &in)",    asFUNCTIONPR(operator>=, (const wstring &, const wstring &), bool), asCALL_CDECL); assert( r >= 0 );
	//r = engine->RegisterGlobalBehaviour(asBEHAVE_LESSTHAN,    "bool f(const wstring &in, const wstring &in)",    asFUNCTIONPR(operator <, (const wstring &, const wstring &), bool), asCALL_CDECL); assert( r >= 0 );
	//r = engine->RegisterGlobalBehaviour(asBEHAVE_GREATERTHAN, "bool f(const wstring &in, const wstring &in)",    asFUNCTIONPR(operator >, (const wstring &, const wstring &), bool), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("wstring", "wstring@ opAdd(const wstring &in) const",asFUNCTIONPR(operator+, (const CScriptWString &, const CScriptWString &), CScriptWString*), asCALL_CDECL_OBJFIRST);assert( r >= 0 );
	//r = engine->RegisterGlobalBehaviour(asBEHAVE_ADD,         "wstring@ f(const wstring &in, const wstring &in)", asFUNCTIONPR(operator +, (const CScriptWString &, const CScriptWString &), CScriptWString*), asCALL_CDECL); assert( r >= 0 );

	// Register the index operator, both as a mutator and as an inspector
	r = engine->RegisterObjectBehaviour("wstring", asBEHAVE_INDEX, "uint8 &f(uint)", asFUNCTION(StringCharAt), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectBehaviour("wstring", asBEHAVE_INDEX, "const uint8 &f(uint) const", asFUNCTION(StringCharAt), asCALL_CDECL_OBJLAST); assert( r >= 0 );

	// Register the object methods
	if( sizeof(size_t) == 4 )
	{
		r = engine->RegisterObjectMethod("wstring", "uint length() const", asMETHOD(wstring,size), asCALL_THISCALL); assert( r >= 0 );
		r = engine->RegisterObjectMethod("wstring", "void resize(uint)", asMETHODPR(wstring,resize,(size_t),void), asCALL_THISCALL); assert( r >= 0 );
	}
	else
	{
		r = engine->RegisterObjectMethod("wstring", "uint64 length() const", asMETHOD(wstring,size), asCALL_THISCALL); assert( r >= 0 );
		r = engine->RegisterObjectMethod("wstring", "void resize(uint64)", asMETHODPR(wstring,resize,(size_t),void), asCALL_THISCALL); assert( r >= 0 );
	}

	// TODO: Add factory  wstring(const wstring &in str, int repeatCount)

	// TODO: Add explicit type conversion via constructor and value cast

	// TODO: Add parseInt and parseDouble. Two versions, one without parameter, one with an outparm that returns the number of characters parsed.

	// Automatic conversion from values
	r = engine->RegisterObjectMethod("wstring", "wstring &opAssign(double)",asFUNCTION(AssignDoubleToString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("wstring", "wstring& opAddAssign(double)",asFUNCTION(AddAssignDoubleToString), asCALL_CDECL_OBJLAST);assert( r >= 0 );
	r = engine->RegisterObjectMethod("wstring", "wstring@ opAdd(double)",asFUNCTION(AddStringDouble), asCALL_CDECL_OBJFIRST);assert( r >= 0 );
	r = engine->RegisterObjectMethod("wstring", "wstring@ opAdd_r(double)",asFUNCTION(AddDoubleString), asCALL_CDECL_OBJLAST);assert( r >= 0 );

	r = engine->RegisterObjectMethod("wstring", "wstring &opAssign(float)",asFUNCTION(AssignFloatToString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	//r = engine->RegisterObjectBehaviour("wstring", asBEHAVE_ASSIGNMENT, "wstring &f(float)", asFUNCTION(AssignFloatToString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("wstring", "wstring& opAddAssign(float)",asFUNCTION(AddAssignFloatToString), asCALL_CDECL_OBJLAST);assert( r >= 0 );
	//r = engine->RegisterObjectBehaviour("wstring", asBEHAVE_ADD_ASSIGN, "wstring &f(float)", asFUNCTION(AddAssignFloatToString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("wstring", "wstring@ opAdd(float)",asFUNCTION(AddStringFloat), asCALL_CDECL_OBJFIRST);assert( r >= 0 );
	//r = engine->RegisterGlobalBehaviour(asBEHAVE_ADD,         "wstring@ f(const wstring &in, float)", asFUNCTION(AddStringFloat), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("wstring", "wstring@ opAdd_r(float)",asFUNCTION(AddFloatString), asCALL_CDECL_OBJLAST);assert( r >= 0 );
	//r = engine->RegisterGlobalBehaviour(asBEHAVE_ADD,         "wstring@ f(float, const wstring &in)", asFUNCTION(AddFloatString), asCALL_CDECL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("wstring", "wstring& fromFloat(float,uint)", asFUNCTION(AssignFloatToStringDigits), asCALL_CDECL_OBJLAST); assert( r >= 0 );


	r = engine->RegisterObjectMethod("wstring", "wstring &opAssign(int)",asFUNCTION(AssignIntToString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("wstring", "wstring& opAddAssign(int)",asFUNCTION(AddAssignIntToString), asCALL_CDECL_OBJLAST);assert( r >= 0 );
	r = engine->RegisterObjectMethod("wstring", "wstring@ opAdd(int)",asFUNCTION(AddStringInt), asCALL_CDECL_OBJFIRST);assert( r >= 0 );
	r = engine->RegisterObjectMethod("wstring", "wstring@ opAdd_r(int)",asFUNCTION(AddIntString), asCALL_CDECL_OBJLAST);assert( r >= 0 );

	r = engine->RegisterObjectMethod("wstring", "wstring &opAssign(uint)",asFUNCTION(AssignUIntToString), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("wstring", "wstring& opAddAssign(uint)",asFUNCTION(AddAssignUIntToString), asCALL_CDECL_OBJLAST);assert( r >= 0 );
	r = engine->RegisterObjectMethod("wstring", "wstring@ opAdd(uint)",asFUNCTION(AddStringUInt), asCALL_CDECL_OBJFIRST);assert( r >= 0 );
	r = engine->RegisterObjectMethod("wstring", "wstring@ opAdd_r(uint)",asFUNCTION(AddUIntString), asCALL_CDECL_OBJLAST);assert( r >= 0 );

	r = engine->RegisterObjectMethod("wstring", "float toFloat()", asFUNCTION(wsting2value<float>), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("wstring", "bool isFloat()", asFUNCTION(canWstingCast2value<float>), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("wstring", "int toInt()", asFUNCTION(wsting2value<int>), asCALL_CDECL_OBJLAST); assert( r >= 0 );
	r = engine->RegisterObjectMethod("wstring", "bool isInt()", asFUNCTION(canWstingCast2value<int>), asCALL_CDECL_OBJLAST); assert( r >= 0 );
}



void RegisterScriptWString(asIScriptEngine *engine)
{
	RegisterScriptWString_Native(engine);
}

END_AS_NAMESPACE


